package protocols

import (
	"encoding/gob"
	"fmt"
	"io"
	"net"
	"os"

	"gitee.com/sjy/selfSendfile/common"
	"github.com/astaxie/beego/logs"
	"github.com/cheggaaa/pb/v3"
)

var _ SendFile = &Send_TCP{}

type Send_TCP struct {
}

// Send implements SendFile.
func (s *Send_TCP) Send(file *common.FileInfo, conf *common.Config) {
	if file.IsEmpty() {
		panic("SendFromPaths is empty! please check config file!")
	}

	// 创建连接
	conn := s.tcp(conf)
	defer conn.Close()

	// 发文件信息
	s.sendFileInfo(conn, file)

	// 发送文件
	s.sendFile(conn, file)
}

func (s *Send_TCP) tcp(conf *common.Config) net.Conn {
	conn, err := net.Dial("tcp", conf.GetServerAddr())
	if err != nil {
		panic(err)
	}
	return conn
}

func (s *Send_TCP) sendFileInfo(conn net.Conn, file *common.FileInfo) {
	// 发出文件信息
	encoder := gob.NewEncoder(conn)
	err := encoder.Encode(file)
	if err != nil {
		panic(fmt.Errorf("send file error: %v", err))
	}

	// 确认服务端收到文件信息
	var confirmation string
	decoder := gob.NewDecoder(conn)
	err = decoder.Decode(&confirmation)
	if err != nil || confirmation != "ok" {
		panic(fmt.Errorf("receive confirmation fileinfo error: %v \n server: %v", err, confirmation))
	}

}

func (s *Send_TCP) sendFile(conn net.Conn, file *common.FileInfo) {

	prossess(conn, file)

	// 确认服务端收到文件数据
	decoder := gob.NewDecoder(conn)

	var confirmation string
	err := decoder.Decode(&confirmation)

	if err != nil {
		panic(fmt.Errorf("receive confirmation error: %v", err))
	}
	logs.Info("ok! from server: %v", confirmation)
}

func prossess(conn net.Conn, file *common.FileInfo) {
	// copyn 可以指定发送的字节数
	// copybuffer 可以指定缓冲区 CopyBuffer(dst Writer, src Reader, buf []byte)
	// _, err := io.Copy(conn, file)
	fileObj, err := os.Open(file.CurrentPath)
	if err != nil {
		panic(fmt.Errorf("open file error: %v;\n %v", file.CurrentPath, err))
	}
	defer fileObj.Close()

	// 创建进度条
	bar := pb.Full.Start64(file.Size)
	barReader := bar.NewProxyReader(fileObj)
	defer bar.Finish()

	// 使用 io.Copy 通过缓冲区复制数据，同时更新进度条
	_, err = io.Copy(conn, barReader)
	if err != nil {
		panic(fmt.Errorf("send file data error: %v", err))
	}

	// 关闭写入方向，告知服务端不再有更多数据要发送
	if tcpConn, ok := conn.(*net.TCPConn); ok {
		if err := tcpConn.CloseWrite(); err != nil {
			panic(fmt.Errorf("close write error: %v", err))
		}
	}
}
